using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using BlobNet.Base.DataProviders;
using BlobNet.Base.Enums;
using BlobNet.FunctionalTests.Dummy;
using BlobNet.TestsCommon.DataProviderHelpers;
using BlobNet.TestsCommon.DataProviderHelpers.Base;
using BlobNet.TestsCommon.Utils;
using System.Linq;
using NUnit.Framework;

namespace BlobNet.FunctionalTests.Base
{
    public abstract class FunctionalTestsBase
    {
        protected abstract DataProviderTestHelper DataProviderTestHelper();

        #region Setup/Teardown

        [SetUp]
        public void TestStartUp()
        {

        }

        [TearDown]
        public void TestTearDown()
        {
            if (expected != null)
                expected.ReleaseStatusResources();
        }

        #endregion

        private FunctionalTestHelper _helper = null;
        private FunctionalTestHelper FunctionalTestHelper
        {
            get { return _helper ?? (_helper = new FunctionalTestHelper(DataProviderTestHelper())); }
        }

        private DummyBlobDomainHolder expected;

        [TestFixtureSetUp]
        public void StartUp()
        {

        }

        [SetUp]
        public void SetUp()
        {
            DataProviderFactory.Provider = null;
        }

        [Test]
        public void Can_Create_Populated_BlobDomain_HolderAsPersistent()
        {
            expected = FunctionalTestHelper.CreatePopulatedBlobDomainHolder(true, true);
            Assert.IsTrue(expected.StatusStorage == StatusStorageType.Persistent);

            Guid entityId = expected.EntityId;

            var actual = new DummyBlobDomainHolder(entityId);

            FunctionalTestHelper.CheckBlobDomainAreEqual(expected, actual);
        }



        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Get_ItemsByType(StatusStorageType storageType)
        {

            expected = FunctionalTestHelper.CreatePopulatedBlobDomainHolder(true, storageType==StatusStorageType.Persistent);

            var target = expected;

            List<DummyBlobPartA> dbpA = target.GetItems<DummyBlobPartA>();
            Assert.IsNotNull(dbpA);
            Assert.IsTrue(dbpA.Count == 2);
            Assert.IsTrue(dbpA.Count(x => x.EntityId == target.CollectionProperty[0].EntityId) == 1);
            Assert.IsTrue(dbpA.Count(x => x.EntityId == target.CollectionProperty[1].EntityId) == 1);


            List<DummyBlobPartB> dbpB = target.GetItems<DummyBlobPartB>();
            Assert.IsNotNull(dbpB);
            Assert.IsTrue(dbpB.Count == 9);


        }

        [Test]
        public void Can_Create_Populated_Memory_BlobDomain_Holder_Fully_Dettached()
        {
            expected = FunctionalTestHelper.CreatePopulatedBlobDomainHolder(false);

            FunctionalTestHelper.CheckDefaultBlobDomainHolderContent(expected, true);
        }

        [Test]
        public void Cant_Create_Populated_Persistent_BlobDomain_Holder_Fully_Dettached()
        {
            Assert.Throws(typeof(InvalidOperationException), () => FunctionalTestHelper.CreatePopulatedBlobDomainHolder(false, true));

        }

        [Test]
        public void Can_Create_Populated_BlobDomain_Holder_InMemory()
        {
            expected = FunctionalTestHelper.CreatePopulatedBlobDomainHolder(true);

            FunctionalTestHelper.CheckDefaultBlobDomainHolderContent(expected, false);

            FunctionalTestHelper.CheckDefaultBlobDomainHolderDataProviderContent(expected, false, false);

            expected.ReleaseStatusResources();
        }

        [Test]
        public void Can_Create_Populated_BlobDomain_Holder_InMemory_When_No_DataProviderConfig()
        {
            string appSetting = ConfigurationManager.AppSettings["DataProviderClass"];
            ConfigurationManager.AppSettings["DataProviderClass"] = null;
            try
            {
                expected = FunctionalTestHelper.CreatePopulatedBlobDomainHolder(true);

                FunctionalTestHelper.CheckDefaultBlobDomainHolderContent(expected, false);

                expected.ReleaseStatusResources();
            }
            catch (Exception ex)
            {
                ConfigurationManager.AppSettings["DataProviderClass"] = appSetting;

                throw ex;
            }
            finally
            {
                ConfigurationManager.AppSettings["DataProviderClass"] = appSetting;
            }
        }


        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Reasign_Values(StatusStorageType storageType)
        {
            expected = FunctionalTestHelper.CreatePopulatedBlobDomainHolder(true);
            expected.StatusStorage = storageType;
            var objectProperty = FunctionalTestHelper.CreateBlobPartB(expected, true);
            objectProperty.StringProperty = "qwerty";
            expected.BlobPartB = objectProperty;

            const int intProperty = 321654;
            expected.IntProperty = intProperty;

            var listProperty = FunctionalTestHelper.GetBlobPartACollection(expected, true);
            listProperty[0].BlobPartB = objectProperty;
            expected.CollectionProperty = listProperty;

            Assert.AreEqual(expected.BlobPartB.StringProperty, objectProperty.StringProperty);
            Assert.AreEqual(expected.IntProperty, intProperty);
            Assert.AreEqual(expected.CollectionProperty[0].BlobPartB.StringProperty, objectProperty.StringProperty);
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Serialize_And_Reasign_Values(StatusStorageType storageType)
        {
            expected = FunctionalTestHelper.CreatePopulatedBlobDomainHolder(true);
            expected.StatusStorage = storageType;
            MemoryStream memoryStream = TestsHelper.Serialize(expected);
            expected = TestsHelper.Deserialize<DummyBlobDomainHolder>(memoryStream);

            var objectProperty = FunctionalTestHelper.CreateBlobPartB(expected, true);
            objectProperty.StringProperty = "qwerty";
            expected.BlobPartB = objectProperty;

            const int intProperty = 321654;
            expected.IntProperty = intProperty;

            var listProperty = FunctionalTestHelper.GetBlobPartACollection(expected, true);
            listProperty[0].BlobPartB = objectProperty;
            expected.CollectionProperty = listProperty;

            Assert.AreEqual(expected.BlobPartB.StringProperty, objectProperty.StringProperty);
            Assert.AreEqual(expected.IntProperty, intProperty);
            Assert.AreEqual(expected.CollectionProperty[0].BlobPartB.StringProperty, objectProperty.StringProperty);
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Dettach_Item_When_Leaf(StatusStorageType storageType)
        {
            throw new NotImplementedException();
            expected = FunctionalTestHelper.CreatePopulatedBlobDomainHolder(true);
            expected.StatusStorage = storageType;


            //delete leaf

            //check relations from others to this
            
        }


        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Dettach_Item_When_Relation_To_Collection_With_No_More_Refs(StatusStorageType storageType)
        {
            //create new target object for the test
           throw new NotImplementedException();
            //collection disappears, but not its content

            //the relation dissapeared

        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Dettach_Item_When_Relation_To_Collection_With_More_Refs(StatusStorageType storageType)
        {
            throw new NotImplementedException();
            //collection doesnt dissapear


            //the relation dissapeared
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void TwoInstancesOfTheSameBackedBlobPartKeepDataConsistent(StatusStorageType storageType)
        {
            throw new NotImplementedException();
        }

        
        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Update_Relation_When_1_1(StatusStorageType storageType)
        {
            throw new NotImplementedException("Implement for each storage");
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Update_Relation_When_n_1(StatusStorageType storageType)
        {
            throw new NotImplementedException();
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Update_Relation_When_1_n(StatusStorageType storageType)
        {
            throw new NotImplementedException();
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Update_Relation_When_n_m(StatusStorageType storageType)
        {
            throw new NotImplementedException();
        }

        

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Start_Transaction(StatusStorageType storageType)
        {
            throw new NotImplementedException();
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Commit_Transaction(StatusStorageType storageType)
        {
            throw new NotImplementedException();
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Rollback_Transaction(StatusStorageType storageType)
        {
            throw new NotImplementedException();
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Specify_One_Single_Db_For_All_Contexts(StatusStorageType storageType)
        {
            throw new NotImplementedException();
        }

        [Test]
        [TestCase(StatusStorageType.Memory)]
        [TestCase(StatusStorageType.Persistent)]
        public void Can_Specify_One_Single_Db_Per_Each_Context(StatusStorageType storageType)
        {
            throw new NotImplementedException();
        }

    }
}